package intjavatorpedo.client;

import intjavatorpedo.db.BombResult;
import intjavatorpedo.db.Coord;
import intjavatorpedo.db.MyTurnResult;
import intjavatorpedo.db.Ship;
import intjavatorpedo.db.UserTable;
import intjavatorpedo.ws.TorpedoInterface;

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.util.List;

import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

public class MainWindow extends JFrame {

	private TorpedoInterface torpedo;
	private int gameNumber;
	private String userName;
	private String opponentUserName;

	private JPanel panelLeft;
	private JPanel panelRight;
	private TorpedoButton leftButtons[][];
	private TorpedoButton rightButtons[][];
	private boolean myTurn;

	private JLabel infoLabel;

	/**
	 * Create the application.
	 */
	public MainWindow(TorpedoInterface torpedo, int gameNumber, String userName, String opponentUserName) {
		this.torpedo = torpedo;
		this.gameNumber = gameNumber;
		this.userName = userName;
		this.opponentUserName = opponentUserName;
		this.myTurn = true;

		this.setTitle(userName + " vs " + opponentUserName);
		initComponents();
		startGame();
	}

	/**
	 * Initialize the contents of the frame.
	 */
	private void initComponents() {
		setBounds(100, 100, 700, 500);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		getContentPane().setLayout(new BorderLayout(0, 0));

		panelLeft = new JPanel();
		getContentPane().add(panelLeft, BorderLayout.WEST);

		panelRight = new JPanel();
		getContentPane().add(panelRight, BorderLayout.EAST);

		infoLabel = new JLabel();
		getContentPane().add(infoLabel, BorderLayout.CENTER);
		infoLabel.setVisible(false);
		this.infoLabel.setMinimumSize(new Dimension(120, 20));
	}

	private void initTables(int rows, int cols) {
		panelLeft.setLayout(new GridLayout(rows, cols, 0, 0));
		leftButtons = new TorpedoButton[rows][cols];
		for (int r = 0; r < rows; r++) {
			for (int c = 0; c < cols; c++) {
				TorpedoButton tb = new TorpedoButton(this, r, c, false);
				tb.refreshStatusMyCoord(false, false);
				tb.setEnabled(false);
				panelLeft.add(tb);
				leftButtons[r][c] = tb;
			}
		}

		panelRight.setLayout(new GridLayout(rows, cols, 0, 0));
		rightButtons = new TorpedoButton[rows][cols];
		for (int r = 0; r < rows; r++) {
			for (int c = 0; c < cols; c++) {
				TorpedoButton tb = new TorpedoButton(this, r, c, true);
				tb.refreshStatusOpponentCoord(false, false);
				panelRight.add(tb);
				rightButtons[r][c] = tb;
			}
		}
		
		this.pack();
		Dimension curSize = this.getSize();
		this.setSize(curSize.width + 150, curSize.height);
		this.setResizable(false);
	}

	private void refreshTables() {
		UserTable userTable = torpedo.getGameStatus(gameNumber, userName);

		// Go through all opponent guesses
		List<Coord> opponentGuesses = userTable.getOpponentGuesses();
		for (Coord c : opponentGuesses) {
			// We need only the unsuccessful ones
			// We assume that this is not a ship part (it can be) and it is bombed (successfully or unsuccessfully)
			leftButtons[c.getRow()][c.getCol()].refreshStatusMyCoord(false, true);
		}

		// Go through all ships, overwrite some cells
		List<Ship> ships = userTable.getShips();
		for (Ship s : ships) {
			List<Coord> coords = s.getCoords();
			for (Coord c : coords) {
				leftButtons[c.getRow()][c.getCol()].refreshStatusMyCoord(true,
						c.isSuccesfullyBombed());
			}
		}

		// Go through all guesses
		List<Coord> guesses = userTable.getGuesses();
		for (Coord c : guesses) {
			rightButtons[c.getRow()][c.getCol()].refreshStatusOpponentCoord(true,
					c.isSuccesfullyBombed());
		}
	}
	
	private void lockScreen() {
		// Display message and lock screen
		this.infoLabel.setText("Waiting for other user's move, screen locked...");
		this.infoLabel.setVisible(true);
		this.setEnabledForRightButtons(false);
	}
	
	private void unlockScreen() {
		// Display message and unlock screen
		this.infoLabel.setText("Click a cell at the right side to bomb");
		this.infoLabel.setVisible(true);
		this.setEnabledForRightButtons(true);
	}

	private void startGame() {
		infoLabel.setText("Waiting for other users...");
		infoLabel.setVisible(true);
		
		new Thread(new Runnable() {
			public void run() {
				try {
					// Checking that other user is connected
					while (true) {
						boolean otherUserStarted = torpedo.otherUserStarted(
								gameNumber, userName);
						if (otherUserStarted)
							break;

						Thread.sleep(1000);
					}

					final UserTable userTable = torpedo.getGameStatus(
							gameNumber, userName);
					EventQueue.invokeLater(new Runnable() {
						public void run() {
							try {
								initTables(userTable.getRows(),
										userTable.getCols());
								refreshTables();
								
								// If not our turn, lock screen
								if (torpedo.isMyTurn(gameNumber, userName) == MyTurnResult.NO) {
									lockScreen();
									waitForOtherPlayer();
								} else
									unlockScreen();
							} catch (Exception e) {
								e.printStackTrace();
							}
						}
					});

					JOptionPane.showMessageDialog(getContentPane(),
							"The game began, your ships were placed randomly, choose a coordinate to bomb.");
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}).start();
	}
	
	private void setEnabledForRightButtons(boolean enabled) {
		for (int r = 0; r < this.rightButtons.length; r++) {
			for (int c = 0; c < this.rightButtons[r].length; c++) {
				rightButtons[r][c].setEnabled(enabled);
			}
		}
	}
	
	private void waitForOtherPlayer() {
		// Waiting for other player
		new Thread(new Runnable() {
			public void run() {
				try {
					// Polling isMyTurn()
					while (true) {
						MyTurnResult myTurn = torpedo.isMyTurn(gameNumber, userName);
						if (myTurn == MyTurnResult.YES)
							break;
						else if (myTurn == MyTurnResult.LOST) {
							JOptionPane.showMessageDialog(getContentPane(),
									"You have lost! Press OK to close application");
							dispose();
							System.exit(1);
						}

						Thread.sleep(1000);
					}
					
					refreshTables();
					repaint();
					
					myTurn = true;
					unlockScreen();
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}).start();
	}

	synchronized public void buttonInput(int row, int col, boolean opponent) {
		if (!myTurn)
			return;
		
		// Only bomb if user clicked on an opponent button
		if (opponent) {
			BombResult res = torpedo.userGuess(gameNumber, userName, row, col);
			myTurn = false;
			
			refreshTables();
			this.repaint();
			
			if (res == BombResult.SUCCESSFUL || res == BombResult.SANK) {
				if (torpedo.userWon(gameNumber, userName)) {
					JOptionPane.showMessageDialog(getContentPane(),
							"You have won! Press OK to close application");
					this.dispose();
					System.exit(1);
				} else if (res == BombResult.SANK) {
					JOptionPane.showMessageDialog(getContentPane(),
							"You have successfully sunk an opponent ship.");
				}
			}
			
			
			lockScreen();
			
			waitForOtherPlayer();
		}
	}
}
